﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace com.ReinforceLab.AudioModem.FSK
{
    class FSKModulator
    {
        #region Variables        
        readonly IAudioPHY  _phy;
        readonly IPacketSource _packetSource;
        readonly short[] _mark0, _mark1;        
        readonly short[] _waveform;
        byte[] _packet;
        int _packetPtr, _packetLength;
        #endregion

        #region Constructor
        public FSKModulator(IAudioPHY phy,IPacketSource packetSource, int mark1SamplePerCycle)
        {
            if (phy == null)
                throw new ArgumentNullException("phy");
            if (packetSource == null)
                throw new ArgumentNullException("packetSource");
            if (mark1SamplePerCycle < 2)
                throw new ArgumentOutOfRangeException("mark1SamplePerCycle");

            _phy = phy;
            _packetSource = packetSource;

            short amplitude = short.MaxValue;
            double dw = 2 * Math.PI / (double)mark1SamplePerCycle;

            // initialize mark1 waveform
            _mark1 = new short[mark1SamplePerCycle];            
            for (int i = 0; i < mark1SamplePerCycle; i++)
                _mark1[i] = (short)(amplitude * Math.Sin(dw * i));
            
            // initialize mark0 waveform
            dw = dw / 2;
            _mark0 = new short[mark1SamplePerCycle * 2];
            for (int i = 0; i < mark1SamplePerCycle * 2; i++)
                _mark0[i] = (short)(amplitude * Math.Sin(dw * i));
            
            _waveform    = new short[_phy.BufferLength];
            
            _packet       = new byte[] { };
            _packetPtr    = 0;
            _packetLength = 0;
        }
        #endregion

        #region IAudioConnector メンバ
        public short[] Send(out int validLength)
        {
            // clear waveform
            Array.Clear(_waveform, 0, _waveform.Length); // sizeof(short) = 2
            
            // fill packet data
            if (_packetPtr == _packetLength)
            { 
                _packet    = _packetSource.SendPacket(out _packetLength);
                _packetPtr = 0;
            }
            
            // fill modulated signal
            int index = 0;
            while (index < _phy.MinimumBufferFillLength && _packetPtr < _packetLength)
            {
                // start bit '0', data bits, stop bit '1'
                int b = ((int)_packet[_packetPtr++] << 1) | 0x0600;
                for (int i = 0; i < (8 + 3); i++)
                {
                    short[] wave = ((0x01 & b) == 0) ? _mark0 : _mark1;
                    b = (b >> 1);
                    Buffer.BlockCopy(wave, 0, _waveform, index * 2, wave.Length * 2); // sizeof(short) = 2
                    index += wave.Length;
                }                
            }

            // clear packet if it has been send
            if (_packetPtr == _packetLength)
            {
                _packetPtr = 0;
                _packetLength = 0;                
            }

            // return waveform
            validLength =  Math.Max(_phy.MinimumBufferFillLength, index);
            return _waveform;
        }
        #endregion
    }
}
